import { RedisKeyEnum } from "@/common/enums/redis-key.enum";
import { LoggerService } from "@/common/logger/logger.service";
import { RedisService } from "@/common/redis/redis.service";
import { handleError } from "@/utils/handle.error";
import { HttpService } from "@nestjs/axios";
import { BadRequestException, Injectable } from "@nestjs/common";
import { firstValueFrom } from "rxjs";
import { DailyRoutineDto } from "./dto/daily-routine.dto";
import { Request } from "express";
import { TUser } from "@/@types/common";
import { Cacheable, CacheEvict } from "@/common/decorators/cache.decorator";
import { compareTime, previousDay, today } from "@/utils/date";
import { AccountService } from "@/common/account/account.service";

@Injectable()
export class FeishuService {
	private readonly BASE_URL = "https://open.feishu.cn/open-apis";

	constructor(
		private readonly logger: LoggerService,
		private readonly httpService: HttpService,
		private readonly redisService: RedisService,
		private readonly accountService: AccountService,
	) {
		this.logger.setContext(FeishuService.name);
	}

	/** 1. 获取 Tenant Access Token */
	async getTenantAccessToken(
		appId: string,
		appSecret: string,
	): Promise<string> {
		try {
			this.logger.log(`${this.getTenantAccessToken.name} was called`);
			const url = `${this.BASE_URL}/auth/v3/tenant_access_token/internal`;
			const response = await firstValueFrom(
				this.httpService.post(url, { app_id: appId, app_secret: appSecret }),
			);
			return response.data.tenant_access_token;
		} catch (error) {
			handleError(this.logger, error, {
				common: "获取Tenant Access Token失败",
			});
		}
	}

	/** 获取电子表格token信息 spreadsheetToken: 表格尾部后缀 */
	@Cacheable(RedisKeyEnum.FeishuKey)
	async getExcelInfo(spreadsheetToken: string, tenantAccessToken: string) {
		try {
			this.logger.log(`${this.getExcelInfo.name} was called`);
			const url = `${this.BASE_URL}/sheets/v3/spreadsheets/${spreadsheetToken}`;
			const response = await firstValueFrom(
				this.httpService.get(url, {
					headers: { Authorization: `Bearer ${tenantAccessToken}` },
				}),
			);
			console.log(response, "response");
			return response.data.data;
		} catch (error) {
			handleError(this.logger, error, {
				common: "获取电子表格信息失败",
			});
		}
	}

	/** 2. 获取电子表格所有工作表 */
	@Cacheable(RedisKeyEnum.FeishuKey)
	async getSheetInfo(spreadsheetToken: string, tenantAccessToken: string) {
		try {
			this.logger.log(`${this.getSheetInfo.name} was called`);
			const url = `${this.BASE_URL}/sheets/v3/spreadsheets/${spreadsheetToken}/sheets/query`;
			const response = await firstValueFrom(
				this.httpService.get(url, {
					headers: { Authorization: `Bearer ${tenantAccessToken}` },
				}),
			);
			return response.data.data.sheets;
		} catch (error) {
			handleError(this.logger, error, {
				common: "获取电子表格所有工作表信息失败",
			});
		}
	}

	/** 3. 读取电子表格数据 */
	@Cacheable(RedisKeyEnum.FeishuKey)
	async readSheetData(
		spreadsheetToken: string,
		range: string,
		tenantAccessToken: string,
	) {
		try {
			this.logger.log(`${this.readSheetData.name} was called`);
			const url = `${this.BASE_URL}/sheets/v2/spreadsheets/${spreadsheetToken}/values/${range}`;
			const response = await firstValueFrom(
				this.httpService.get(url, {
					headers: { Authorization: `Bearer ${tenantAccessToken}` },
					params: { dateTimeRenderOption: "FormattedString" }, // 返回格式化后的日期
				}),
			);
			return response.data.data;
		} catch (error) {
			handleError(this.logger, error, {
				common: "读取电子表格工作表信息失败",
			});
		}
	}

	/** 4. 更新电子表格数据 */
	@CacheEvict(RedisKeyEnum.FeishuKey)
	async updateSheetData(
		spreadsheetToken: string,
		range: string,
		values: any[][],
		tenantAccessToken: string,
	) {
		try {
			this.logger.log(`${this.updateSheetData.name} was called`);
			const url = `${this.BASE_URL}/sheets/v2/spreadsheets/${spreadsheetToken}/values`;
			const response = await firstValueFrom(
				this.httpService.put(
					url,
					{ valueRange: { range, values } },
					{ headers: { Authorization: `Bearer ${tenantAccessToken}` } },
				),
			);
			return response.data;
		} catch (error) {
			handleError(this.logger, error, {
				common: "更新电子表格工作表信息失败",
			});
		}
	}

	/** 获取打卡表数据 */
	async getDailyDataByDate(user: TUser, date = previousDay()) {
		try {
			this.logger.log(`${this.getDailyDataByDate.name} was called`);
			const { spreadsheetToken, feishuAppid, feishuSecret, sheetName } =
				await this.accountService.getExtraInfo();
			const { name } = user;
			const tenantAccessToken = await this.getTenantAccessToken(
				feishuAppid,
				feishuSecret,
			);
			const sheetsList = await this.getSheetInfo(
				spreadsheetToken,
				tenantAccessToken,
			);
			const sheet_id = sheetsList.find(
				(item) => item.title === sheetName,
			)?.sheet_id;

			// 1. 获取表格数据
			const sheetDataRes = await this.readSheetData(
				spreadsheetToken,
				sheet_id,
				tenantAccessToken,
			);
			let sheetData = sheetDataRes.valueRange.values;
			sheetData = sheetData.filter((item) => item.some((j) => j));

			// 2. 找到日期和名称的位置信息
			const dateRowIndex = sheetData.findIndex((row) =>
				compareTime(row[0], date),
			); // 日期在第一列
			const nameColIndex = sheetData[1]?.findIndex((col) => col === name); // 名称在第二行

			if (nameColIndex === -1) {
				throw new BadRequestException(`名称 "${user.name}" 在表格中未找到`);
			}

			return {
				sheetData,
				nameColIndex,
				dateRowIndex,
				sheet_id,
				tenantAccessToken,
				spreadsheetToken,
			};
		} catch (error) {
			handleError(this.logger, error, {
				common: `获取${date}所有数据失败`,
			});
		}
	}

	/** 获取当前日期日常打卡数据 */
	async checkTodayRoutine(req: Request, date = today) {
		try {
			this.logger.log(`${this.checkTodayRoutine.name} was called`);

			const { sheetData, nameColIndex, dateRowIndex } =
				await this.getDailyDataByDate(req.user as TUser, date);
			if (dateRowIndex === -1 || nameColIndex === -1 || !sheetData.length) {
				return "";
			}
			return sheetData[dateRowIndex][nameColIndex];
		} catch (error) {
			handleError(this.logger, error, {
				common: `获取${date}打卡数据失败`,
			});
		}
	}

	/** 5. 日常打卡记录 */
	async dailyRoutine(params: DailyRoutineDto, req: Request) {
		try {
			this.logger.log(`${this.dailyRoutine.name} was called`);

			const { value, date } = params;
			const {
				sheetData,
				nameColIndex,
				dateRowIndex,
				sheet_id,
				tenantAccessToken,
				spreadsheetToken,
			} = await this.getDailyDataByDate(req.user as TUser, date);

			// 3. 如果没有此日期，新增一行
			const valueLabel = value ? value : "请假";
			if (dateRowIndex === -1) {
				const newRow = Array(sheetData[1]?.length || 20).fill(""); // 默认列数为 20
				newRow[0] = date; // 第一列填入日期
				newRow[nameColIndex] = valueLabel; // 对应名称列填入数据
				sheetData.push(newRow); // 将新行添加到数据中
			} else {
				// 4. 如果有此日期，直接更新单元格
				sheetData[dateRowIndex][nameColIndex] = valueLabel;
			}
			// 5. 更新表格数据
			const res = await this.updateSheetData(
				spreadsheetToken,
				sheet_id,
				sheetData,
				tenantAccessToken,
			);
			return res;
		} catch (error) {
			handleError(this.logger, error, {
				common: "日常打卡失败",
			});
		}
	}
}
